package ge.telasi.tasks.controller;

import ge.telasi.tasks.TaskException;
import ge.telasi.tasks.model.Group;
import ge.telasi.tasks.model.User;
import ge.telasi.tasks.model.validation.GroupValidator;
import ge.telasi.tasks.model.validation.UserValidator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

/**
 * @author dimitri
 */
public class UserController extends Controller {

    public int getActiveUserCount(EntityManager em) {
        String sql = "SELECT COUNT(u) FROM User u WHERE u.status = 1";
        return ((Long) em.createQuery(sql)
                .getSingleResult())
                .intValue();
    }

    public int getAnyUserCountInStructure(EntityManager em, int structureId) {
        String sql = "SELECT COUNT(u) FROM User u WHERE u.structure.id = :str_id";
        return ((Long) em.createQuery(sql)
                .setParameter("str_id", structureId)
                .getSingleResult())
                .intValue();
    }

    public User find(EntityManager em, String username) {
        return (User) em.createNamedQuery("User.authorize")
                .setParameter("username", username)
                .getSingleResult();
    }

    public List<User> getActiveAdmins(EntityManager em) {
        return em.createNamedQuery("User.activeAdmins").getResultList();
    }

    public void changePasswordAdmin(EntityManager em, User admin, User user, String password) {
        requireAdmin(em, admin);
        User exst = find(em, user.getUsername());
        new UserValidator().validatePasswords(password, password);
        exst.setPassword(password);
    }

    public User authorize(EntityManager em, String username, String password) {
        boolean usernameEmpty = username == null || username.trim().length() == 0;
        boolean passwordEmpty = password == null || password.trim().length() == 0;
        if (usernameEmpty || passwordEmpty) {
            throw new TaskException(getLocaleManager().getMessage("UserController.emptyUsernamePassword"));
        }
        try {
            User user = find(em, username);
            if (!user.matches(password)) {
                throw new TaskException(getLocaleManager().getMessage("UserController.illegalLogin"));
            }
            if (!user.isActive() || user.isNew()) {
                throw new TaskException(getLocaleManager().getMessage("UserController.inactiveUser"));
            }
            return user;
        } catch (NoResultException nrex) {
            throw new TaskException(getLocaleManager().getMessage("UserController.illegalLogin"));
        }
    }

    public User register(EntityManager em, User user, String password) {
        new UserValidator(em).validateFull_server_create(user, password);
        user.setUsername(user.getUsername().toLowerCase());
        user.setPassword(password);
        user.setActive(true);
        if (getActiveUserCount(em) == 0) {
            // register first user as admin
            user.setAdmin(true);
            user.setNew(false);
        } else {
            user.setAdmin(false);
            user.setNew(true);
        }
        user.setNotifyByMobile(false);
        user.setNotifyByEmail(true);
        user.setLastTaskSequence(0);
        user.setStructure(new StructureController().findStructure(em, user.getStructure().getId()));
        em.persist(user);
        return user;
    }

    public List<User> getAllUsers(EntityManager em, User user) {
        return em.createNamedQuery("User.all").getResultList();
    }

    public List<User> getAllActiveUsers(EntityManager em, User user) {
        return em.createNamedQuery("User.allActive").getResultList();
    }

    public User updateUserByAdmin(EntityManager em, User admin, User user) {
        requireAdmin(em, admin);
        boolean himself = admin.getUsername().equals(user.getUsername());
        if (himself && (!user.isAdmin() || !user.isActive())) {
            throw new TaskException("Cannot disable or remove admin privilegies to himself.");
        }
        new UserValidator(em).validateFull_server_update(user);
        User exst = find(em, user.getUsername());
        exst.setFirstName(user.getFirstName());
        exst.setFirstName2(user.getFirstName2());
        exst.setLastName(user.getLastName());
        exst.setLastName2(user.getLastName2());
        exst.setMobile(user.getMobile());
        exst.setNotifyByMobile(user.isNotifyByMobile());
        exst.setEmail(user.getEmail());
        exst.setNotifyByEmail(user.isNotifyByEmail());
        boolean emptyCode = user.getCode() == null || user.getCode().trim().length() == 0;
        exst.setCode(emptyCode ? null : user.getCode());
        exst.setStructure(new StructureController().findStructure(em, user.getStructure().getId()));
        exst.setPosition(user.getPosition());
        exst.setLocation(user.getLocation());
        exst.setWorkPhone(user.getWorkPhone());
        exst.setActive(user.isActive());
        exst.setAdmin(user.isAdmin());
        if (exst.isNew()) {
            boolean createHome = !user.isNew();
            exst.setNew(user.isNew());
        }
        return exst;
    }

    public User changePassword(EntityManager em, User user, String newPassword) {
        new UserValidator().validatePasswords(newPassword, newPassword);
        User exst = find(em, user.getUsername());
        exst.setPassword(newPassword);
        return exst;
    }

    public Group findGroup(EntityManager em, int id) {
        return em.find(Group.class, id);
    }

    public List<Group> getAllGroups(EntityManager em, User user) {
        return em.createNamedQuery("Group.all").getResultList();
    }
    
    public Group createGroup(EntityManager em, User user, Group group) {
        requireAdmin(em, user);
        new GroupValidator().validateFull(group);
        group.setActive(true);
        em.persist(group);
        return group;
    }

    public Group updateGroup(EntityManager em, User user, Group group) {
        requireAdmin(em, user);
        new GroupValidator().validateFull(group);
        Group exst = findGroup(em, group.getId());
        exst.setName(group.getName());
        exst.setDescription(group.getDescription());
        return exst;
    }

    public boolean deleteGroup(EntityManager em, User user, Group group) {
        requireAdmin(em, user);
        Group exst = findGroup(em, group.getId());
        if (isGroupEmpty(em, group) && !isGroupUsedInTask(em, group)) {
            // group is not used anywhere: you can savely delete it
            em.remove(exst);
            return true;
        } else {
            // this group is used: the only thing which can be done is deactivate this group
            exst.setActive(false);
            return false;
        }
    }

    private boolean isGroupEmpty(EntityManager em, Group group) {
        // TODO: whether this group contains users
        return true;
    }

    private boolean isGroupUsedInTask(EntityManager em, Group group) {
        // TODO: whether this group is used in any task
        return false;
    }

    public List<User> getGroupUsers(EntityManager em, Group group) {
        Group exst = findGroup(em, group.getId());
        em.refresh(exst);
        return exst.getUsers();
    }

    public List<Group> getUserGroups(EntityManager em, User user) {
        User exst = find(em, user.getUsername());
        em.refresh(exst);
        return exst.getGroups();
    }

    public void addUsersToGroup(EntityManager em, User admin, Group group, List<User> users) {
        requireAdmin(em, admin);
        Group exst = findGroup(em, group.getId());
        em.refresh(exst);
        for (User user : users) {
            if (!exst.getUsers().contains(user)) {
                User aUser = find(em, user.getUsername());
                exst.getUsers().add(aUser);
            }
        }
    }

    public void removeUsersFromGroup(EntityManager em, User admin, Group group, List<User> users) {
        requireAdmin(em, admin);
        Group exst = findGroup(em, group.getId());
        em.refresh(exst);
        for (User user : users) {
            if (exst.getUsers().contains(user)) {
                exst.getUsers().remove(user);
            }
        }
    }

    public User addRelation(EntityManager em, User admin, User root, List<User> users, boolean assistant) {
        requireAdmin(em, admin);
        root = find(em, root.getUsername());
        em.refresh(root);
        for (User u : users) {
            User user = find(em, u.getUsername());
            if (assistant) {
                if (!root.getAssistants().contains(user)) {
                    root.getAssistants().add(user);
                    user.getBosses().add(root);
                }
            } else {
                if (!root.getBosses().contains(user)) {
                    root.getBosses().add(user);
                    user.getAssistants().add(root);
                }
            }
        }
        return root;
    }

    public User removeRelation(EntityManager em, User admin, User root, List<User> users, boolean assistant) {
        requireAdmin(em, admin);
        root = find(em, root.getUsername());
        em.refresh(root);
        for (User u : users) {
            User user = find(em, u.getUsername());
            if (assistant) {
                if (root.getAssistants().contains(user)) {
                    root.getAssistants().remove(user);
                    user.getBosses().remove(root);
                }
            } else {
                if (root.getBosses().contains(user)) {
                    root.getBosses().remove(user);
                    user.getAssistants().remove(root);
                }
            }
        }
        return root;
    }

    public long uploadUserImage(EntityManager em, User user, byte[] data) {
        if (user.getBinaryId() != null) {
            long id = user.getBinaryId();
            new BinaryDataController().updateBinaryData(em, id, data);
            return id;
        } else {
            long id = new BinaryDataController().createBinaryData(em, data);
            user.setBinaryId(id);
            return id;
        }
    }

}
